using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using RulesEngine;

namespace Polymod
{
    public class ValidationModel<T> : NotificationModel<T>, IDataErrorInfo //TODO: Use .net 4.5 to use better datarerrorinfo!!
    {
        //NOTE: By default, models are in a valid state (even if they violate rules)
        //Consider a blank form - don't want to have all errors highlighted as soon as the form is displayed.
        bool _needsValidation = false;
        IValidationReport _report;
        Engine _engine;


        public override void Initialize(object value, IDictionary<string, object> state)
        {
            base.Initialize(value, state);
            _engine = (Engine)state[Constants.RulesEngine];
        }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get
            {
                var localReport = _report;

                if (!_needsValidation)
                {
                    //TODO: Instead of 'composed validation' use an interface to determine which object in the model tree is validated... Always Root, or Current can be default options...
                    //TODO: To support composed validation (i.e. Rules specified for compositions) should I call validate on the Root of the model?!?
                    if (localReport == null) return null;
                    return GetErrorMessage(localReport, columnName, _engine.DefaultErrorResolver);
                }

                //TODO: Some locking here please (no re-entrant calls etc...)
                _needsValidation = false;
                localReport = new ValidationReport();
                _engine.Validate(this.GetValue(), localReport, ValidationReportDepth.FieldShortCircuit);
                _report = localReport;
                return GetErrorMessage(localReport, columnName, _engine.DefaultErrorResolver);
            }
        }

        private string GetErrorMessage(IValidationReport report, string columnName, IErrorResolver errorResolver)
        {
            var p0 = Expression.Parameter(typeof(T));
            var exp = Expression.PropertyOrField(p0, columnName);
            var lambda = Expression.Lambda(exp, p0);
            var invocation = typeof(IValidationReport).GetMethod("GetErrorMessage").MakeGenericMethod(typeof(T), exp.Type);
            var result = invocation.Invoke(report, new object[] { this.GetValue(), lambda, errorResolver }) as string;
            return result;
        }

        protected override void OnPropertyChanged(string property)
        {
            //NOTE: Important to set _needsValidation BEFORE calling base (some error handler must be listening in on the notifypropertychanged)
            _needsValidation = true;
            base.OnPropertyChanged(property);
        }
    }
}
